/*
 * Copyright 2014 HarryKim.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package opera.channel.handler;

import client.MapleCharacter;
import client.MapleClient;
import client.inventory.Equip;
import client.inventory.Item;
import client.inventory.ItemFlag;
import client.inventory.MapleInventoryType;
import constants.GameConstants;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import server.MapleItemInformationProvider;
import server.Randomizer;
import tools.MaplePacketCreator;
import tools.data.LittleEndianAccessor;

/**
 *
 * @author HarryKim
 */
public class ScrollHandler {

    public static void UseUpgradeScroll(LittleEndianAccessor reader, MapleClient client) {
        MapleCharacter gc = client.getPlayer();
        gc.updateTick(reader.readInt());
        short scrollSlot = reader.readShort();
        short equipSlot = reader.readShort();
        boolean isLegendarySpirit = reader.readByte() != 0;
        MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
        gc.setScrolledPosition((short) 0);
        Item scroll = gc.getInventory(MapleInventoryType.USE).getItem(scrollSlot);
        if (scroll == null) {
            scroll = gc.getInventory(MapleInventoryType.CASH).getItem(scrollSlot);
            if (scroll == null) {
                return;
            }
        }
        int scrollItemID = scroll.getItemId();
        if (scroll.getQuantity() < 1) {
            return;
        }
        Equip equip = null;
        if (equipSlot < 0) {
            equip = (Equip) gc.getInventory(MapleInventoryType.EQUIPPED).getItem(equipSlot);
        } else {
            if (isLegendarySpirit) {
                equip = (Equip) gc.getInventory(MapleInventoryType.EQUIP).getItem(equipSlot);
            }
        }
        if (equip == null) {
            return;
        }
        if (gc.hasBlockedInventory()) {
            return;
        }
        Map<String, Integer> equipStats = ii.getEquipStats(equip.getItemId());
        /**
         * 에반 드래곤 전용 아이템이나 메카닉 전용 아이템은 혼돈의 주문서를 제외한 다른 주문서를 적용하려함.
         */
        if (GameConstants.isEvanDragonItem(scrollItemID) || GameConstants.isMechanicItem(scrollItemID)) {
            if (!GameConstants.isChaosScroll(scrollItemID)) {
                client.getSession().write(MaplePacketCreator.getInventoryFull());
                return;
            }
        }
        /**
         * 내구도가 없는 아이템에 연성서 주문서를 적용하려함.
         */
        if (GameConstants.isTablet(scrollItemID) && equip.getDurability() < 0) {
            client.getSession().write(MaplePacketCreator.getInventoryFull());
            return;
        }
        /**
         * 내구도가 있는 아이템에 일반 주문서를 적용하려함.
         * 
         */
        if (!GameConstants.isTablet(scrollItemID) && equip.getDurability() >= 0) {
            client.getSession().write(MaplePacketCreator.getInventoryFull());
            return;
        }
        if (!GameConstants.isCleanSlate(scrollItemID)
                && !GameConstants.isSpecialScroll(scrollItemID)
                && !GameConstants.isEnhanceScroll(scrollItemID)
                && !GameConstants.isPotentialScroll(scrollItemID)) {
            /**
             * 남은 업그레이드 횟수가 없는 아이템에 일반 주문서를 적용하려함.
             */
            if (equip.getUpgradeSlots() < 1) {
                client.getSession().write(MaplePacketCreator.getInventoryFull());
                return;
            }
        } else {
            /**
             * 캐시아이템에 특수 주문서를 적용하려함.
             */
            if (ii.isCash(equip.getItemId())) {
                client.getSession().write(MaplePacketCreator.getInventoryFull());
                return;
            }
            if (GameConstants.isEnhanceScroll(scrollItemID)) {
                /**
                 * 남은 업그레이드 횟수가 있거나 15강 강화에 성공한 아이템에 장비강화 주문서를 적용하려함.
                 */
                if (equip.getUpgradeSlots() > 0 || equip.getEnhance() >= 15) {
                    client.getSession().write(MaplePacketCreator.getInventoryFull());
                    return;
                }
            } else if (GameConstants.isPotentialScroll(scrollItemID)) {
                /**
                 * 일반등급이 아닌 장비 아이템에 잠재능력 주문서를 적용하려함.
                 */
                if (equip.getState() > 0) {
                    client.getSession().write(MaplePacketCreator.getInventoryFull());
                    return;
                }
            }
        }
        /**
         * 업그레이드가 불가능 하거나 차감된 업그레이드 가능 횟수가 없는 장비에 백의 주문서를 적용 하려함.
         */
        if (GameConstants.isCleanSlate(scrollItemID) && equipStats.containsKey("tuc") && ((equip.getLevel() + equip.getUpgradeSlots()) >= equipStats.get("tuc"))) {
            client.getSession().write(MaplePacketCreator.getInventoryFull());
            return;
        }
        /**
         * 일부 장비아이템에만 적용 가능한 주문서.
         */
        List<Integer> scrollReqs = ii.getScrollReqs(scrollItemID);
        if (scrollReqs != null && scrollReqs.size() > 0 && !scrollReqs.contains(equip.getItemId())) {
            client.getSession().write(MaplePacketCreator.getInventoryFull());
            return;
        }
        ScrollResult result = scrollEquip(equip, equipStats, scroll, gc);
        gc.getInventory(MapleInventoryType.USE).removeItem(scrollSlot, (short) 1, false);
        if (result.equals(ScrollResult.CURSE)) {
            client.getSession().write(MaplePacketCreator.scrolledItem(scroll, equip, true, false));
            if (equipSlot < 0) {
                gc.getInventory(MapleInventoryType.EQUIPPED).removeItem(equipSlot);
            } else {
                gc.getInventory(MapleInventoryType.EQUIP).removeItem(equipSlot);
            }
        } else {
            client.getSession().write(MaplePacketCreator.scrolledItem(scroll, equip, false, false));
        }
        gc.getMap().broadcastMessage(gc, MaplePacketCreator.getScrollEffect(gc.getId(), result, isLegendarySpirit), true);
        if (equipSlot < 0 && result == ScrollResult.CURSE) {
            gc.equipChanged();
        }
    }

    /**
     * 장비 아이템에 주문서 효과를 적용한다.
     *
     * @param equip
     * @param equipStats
     * @param scroll
     * @param gc
     * @return 주문서 효과를 적용한 후의 결과.
     */
    private static ScrollResult scrollEquip(Equip equip, Map<String, Integer> equipStats, Item scroll, MapleCharacter gc) {
        MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
        int scrollItemID = scroll.getItemId();
        Map<String, Integer> scrollStats = ii.getEquipStats(scrollItemID);
        int success = 0;
        if (GameConstants.isEnhanceScroll(scrollItemID)) {
            if (scrollItemID == 2049300) {
                success = Math.max(100 - (equip.getEnhance() * 10), 10);
            } else {
                success = Math.max(80 - (equip.getEnhance() * 10), 10);
            }
        } else if (GameConstants.isPotentialScroll(scrollItemID)) {
            if (scrollItemID == 2049400) {
                success = 90;
            } else {
                success = 70;
            }
        } else if (GameConstants.isTablet(scrollItemID)) {
            // TODO : 연성서
        } else if (scrollStats.containsKey("success")) {
            success = scrollStats.get("success");
        }
        int cursed = 0;
        if (GameConstants.isEnhanceScroll(scrollItemID) || GameConstants.isPotentialScroll(scrollItemID)) {
            cursed = 100;
        } else if (GameConstants.isTablet(scrollItemID)) {
            // TODO : 연성서
        } else if (scrollStats.containsKey("cursed")) {
            cursed = scrollStats.get("cursed");
        }
        if (Randomizer.nextInt(100) <= success) {
            switch (scrollItemID) {
                case 2040727: { // 신발 스파이크 주문서 10%
                    short flag = equip.getFlag();
                    flag |= ItemFlag.SPIKES.getValue();
                    equip.setFlag(flag);
                    break;
                }
                case 2041058: { // 망토 방한 주문서 10%
                    short flag = equip.getFlag();
                    flag |= ItemFlag.COLD.getValue();
                    equip.setFlag(flag);
                    break;
                }
                case 2049000:
                case 2049001: // 백의 주문서 1%
                case 2049002: // 백의 주문서 3%
                case 2049003: // 백의 주문서 5%
                case 2049004: // 백의 주문서 10%
                case 2049005: { // 백의 주문서 20%
                    equip.setUpgradeSlots((byte) (equip.getUpgradeSlots() + 1));
                    break;
                }
                case 2049006: // 저주받은 백의 주문서 1%
                case 2049007: // 저주받은 백의 주문서 3%
                case 2049008: { // 저주받은 백의 주문서 5%
                    equip.setUpgradeSlots((byte) (equip.getUpgradeSlots() + 2));
                    break;
                }
                case 2049100: // 혼돈의 주문서 60%
                case 2049101: // 거짓말쟁이 나무수액 100%
                case 2049102: // 메이플 시럽 100%
                case 2049103: // 비치샌들 전용주문서 100%
                case 2049104: // 요원장비 전용 주문서 100%
                case 2049115: // 혼돈의 라바나 투구 주문서 50%
                case 2049116: { // 놀라운 혼돈의 주문서 60%
                    int chaosNumber = (scrollItemID == 2049116 ? 10 : 5);
                    if (equip.getStr() > 0) {
                        equip.setStr(applyChaos(equip.getStr(), chaosNumber));
                    }
                    if (equip.getDex() > 0) {
                        equip.setDex(applyChaos(equip.getDex(), chaosNumber));
                    }
                    if (equip.getInt() > 0) {
                        equip.setInt(applyChaos(equip.getInt(), chaosNumber));
                    }
                    if (equip.getLuk() > 0) {
                        equip.setLuk(applyChaos(equip.getLuk(), chaosNumber));
                    }
                    if (equip.getWatk() > 0) {
                        equip.setWatk(applyChaos(equip.getWatk(), chaosNumber));
                    }
                    if (equip.getWdef() > 0) {
                        equip.setWdef(applyChaos(equip.getWdef(), chaosNumber));
                    }
                    if (equip.getMatk() > 0) {
                        equip.setMatk(applyChaos(equip.getMatk(), chaosNumber));
                    }
                    if (equip.getMdef() > 0) {
                        equip.setMdef(applyChaos(equip.getMdef(), chaosNumber));
                    }
                    if (equip.getAcc() > 0) {
                        equip.setAcc(applyChaos(equip.getAcc(), chaosNumber));
                    }
                    if (equip.getAvoid() > 0) {
                        equip.setAvoid(applyChaos(equip.getAvoid(), chaosNumber));
                    }
                    if (equip.getSpeed() > 0) {
                        equip.setSpeed(applyChaos(equip.getSpeed(), chaosNumber));
                    }
                    if (equip.getJump() > 0) {
                        equip.setJump(applyChaos(equip.getJump(), chaosNumber));
                    }
                    if (equip.getHp() > 0) {
                        equip.setHp(applyChaos(equip.getHp(), chaosNumber));
                    }
                    if (equip.getMp() > 0) {
                        equip.setMp(applyChaos(equip.getMp(), chaosNumber));
                    }
                    break;
                }
                case 2049300: // 고급 장비강화 주문서
                case 2049301: { // 장비강화 주문서
                    if (equip.getStr() > 0 || Randomizer.nextInt(50) == 1) {
                        equip.setStr(applyEnhance(equip.getStr(), 1));
                    }
                    if (equip.getDex() > 0 || Randomizer.nextInt(50) == 1) {
                        equip.setDex(applyEnhance(equip.getDex(), 1));
                    }
                    if (equip.getInt() > 0 || Randomizer.nextInt(50) == 1) {
                        equip.setInt(applyEnhance(equip.getInt(), 1));
                    }
                    if (equip.getLuk() > 0 || Randomizer.nextInt(50) == 1) {
                        equip.setLuk(applyEnhance(equip.getLuk(), 1));
                    }
                    if (equip.getWatk() > 0 && GameConstants.isWeapon(equip.getItemId())) {
                        equip.setWatk(applyEnhance(equip.getWatk(), 0));
                    }
                    if (equip.getWdef() > 0 || Randomizer.nextInt(40) == 1) {
                        equip.setWdef(applyEnhance(equip.getWdef(), 3));
                    }
                    if (equip.getMatk() > 0 && GameConstants.isWeapon(equip.getItemId())) {
                        equip.setMatk(applyEnhance(equip.getMatk(), 0));
                    }
                    if (equip.getMdef() > 0 || Randomizer.nextInt(40) == 1) {
                        equip.setMdef(applyEnhance(equip.getMdef(), 3));
                    }
                    if (equip.getAcc() > 0 || Randomizer.nextInt(20) == 1) {
                        equip.setAcc(applyEnhance(equip.getAcc(), 3));
                    }
                    if (equip.getAvoid() > 0 || Randomizer.nextInt(20) == 1) {
                        equip.setAvoid(applyEnhance(equip.getAvoid(), 3));
                    }
                    if (equip.getSpeed() > 0 || Randomizer.nextInt(10) == 1) {
                        equip.setSpeed(applyEnhance(equip.getSpeed(), 3));
                    }
                    if (equip.getJump() > 0 || Randomizer.nextInt(10) == 1) {
                        equip.setJump(applyEnhance(equip.getJump(), 3));
                    }
                    if (equip.getHp() > 0 || Randomizer.nextInt(5) == 1) {
                        equip.setHp(applyEnhance(equip.getHp(), 2));
                    }
                    if (equip.getMp() > 0 || Randomizer.nextInt(5) == 1) {
                        equip.setMp(applyEnhance(equip.getMp(), 2));
                    }
                    equip.setEnhance((byte) (equip.getEnhance() + 1));
                    break;
                }
                case 2049400: // 고급 잠재능력 부여 주문서
                case 2049401: { // 잠재능력 부여 주문서
                    equip.resetPotential();
                    break;
                }
                default: {
                    for (Entry<String, Integer> stat : scrollStats.entrySet()) {
                        String key = stat.getKey();
                        switch (key) {
                            case "STR":
                                equip.setStr((short) (equip.getStr() + stat.getValue()));
                                break;
                            case "DEX":
                                equip.setDex((short) (equip.getDex() + stat.getValue()));
                                break;
                            case "INT":
                                equip.setInt((short) (equip.getInt() + stat.getValue()));
                                break;
                            case "LUK":
                                equip.setLuk((short) (equip.getLuk() + stat.getValue()));
                                break;
                            case "PAD":
                                equip.setWatk((short) (equip.getWatk() + stat.getValue()));
                                break;
                            case "PDD":
                                equip.setWdef((short) (equip.getWdef() + stat.getValue()));
                                break;
                            case "MAD":
                                equip.setMatk((short) (equip.getMatk() + stat.getValue()));
                                break;
                            case "MDD":
                                equip.setMdef((short) (equip.getMdef() + stat.getValue()));
                                break;
                            case "ACC":
                                equip.setAcc((short) (equip.getAcc() + stat.getValue()));
                                break;
                            case "EVA":
                                equip.setAvoid((short) (equip.getAvoid() + stat.getValue()));
                                break;
                            case "Speed":
                                equip.setSpeed((short) (equip.getSpeed() + stat.getValue()));
                                break;
                            case "Jump":
                                equip.setJump((short) (equip.getJump() + stat.getValue()));
                                break;
                            case "MHP":
                                equip.setHp((short) (equip.getHp() + stat.getValue()));
                                break;
                            case "MMP":
                                equip.setMp((short) (equip.getMp() + stat.getValue()));
                                break;
                            case "MHPr":
                                equip.setHpR((short) (equip.getHpR() + stat.getValue()));
                                break;
                            case "MMPr":
                                equip.setMpR((short) (equip.getMpR() + stat.getValue()));
                                break;
                            default:
                                break;
                        }
                    }
                    break;
                }
            }
            if (!GameConstants.isCleanSlate(scrollItemID)
                    && !GameConstants.isSpecialScroll(scrollItemID)
                    && !GameConstants.isEnhanceScroll(scrollItemID)
                    && !GameConstants.isPotentialScroll(scrollItemID)) {
                equip.setUpgradeSlots((byte) (equip.getUpgradeSlots() - 1));
                equip.setLevel((byte) (equip.getLevel() + 1));
            }
            return ScrollResult.SUCCESS;
        } else {
            if (!GameConstants.isCleanSlate(scrollItemID)
                    && !GameConstants.isSpecialScroll(scrollItemID)
                    && !GameConstants.isEnhanceScroll(scrollItemID)
                    && !GameConstants.isPotentialScroll(scrollItemID)) {
                equip.setUpgradeSlots((byte) (equip.getUpgradeSlots() - 1));
            }
            if (Randomizer.nextInt(100) < cursed) {
                return ScrollResult.CURSE;
            }
            return ScrollResult.FAIL;
        }
    }

    private static short applyChaos(short source, int chaosNumber) {
        return (short) (source + (Randomizer.nextInt(chaosNumber) * (Randomizer.nextBoolean() ? 1 : -1)));
    }

    /**
     * 장비강화 주문서를 적용 한다. 물리 공격력, 마법 공격력의 경우 1에서 시작하여 각 수치가 50 단위마다 증가 수치가 1씩
     * 증가한다. STR, DEX, INT, LUK의 경우 1에서 시작하여 각 수치가 5 단위마다 증가 수치가 1씩 증가한다. HP,
     * MP의 경우 증가 수치는 10이다. 나머지 옵션의 경우 증가 수치는 5이다.
     *
     * @param source 주문서를 적용할 장비 아이템의 스탯 값.
     * @param type 주문서를 적용할 장비 아이템의 옵션의 종류. 0 : 물리 공격력, 마법 공격력 1 : STR, DEX,
     * INT, LUK 2 : HP, MP 다른 값 : 나머지 옵션들
     * @return 주문서를 적용한 장비 아이템의 변경할 스탯 값
     */
    private static short applyEnhance(short source, int type) {
        int inc;
        switch (type) {
            case 0:
                inc = (source / 50) + 1;
                break;
            case 1:
                inc = (source / 5) + 1;
                break;
            case 2:
                inc = 10;
                break;
            default:
                inc = 5;
                break;
        }
        return (short) (source + Randomizer.nextInt(inc));
    }

    public enum ScrollResult {

        SUCCESS, FAIL, CURSE;
    }
}
